ESLint 支持插件,使用插件是扩展 ESLint 功能的一种方式,可以通过插件来自定义新的规则或者处理器。

使用插件能够带来的好处包括:

插件名称的规范

ESLint 中的插件,每一个插件是一个 npm 包,命名的格式为 eslint-plugin-<插件名称>,如:eslint-plugin-jqueryeslint-plugin-react

插件还可以使用 scope 包的形式:@<scope>/eslint-plugin-<插件名称>,如:@jquery/eslint-plugin-jquery,还可以只有前面的 scope:@jquery/eslint-plugin

npm 官网 上面搜索 eslint-plugin 能够找到很多 ESLint 相关的插件。

使用插件

使用插件的方式

使用现有的插件,方式非常简单,就分为两步:

  1. 安装插件
  2. 在配置文件中进行配置

假设要使用 eslint-plugin-react 插件,首先安装该插件:

pnpm add eslint-plugin-react -D

接下来在配置文件中进行配置:

{
  "plugins": ["react"]
}

配置完成后,就可以在配置文件添置该插件所提供的各种规则:

{
  "plugins": [
    "react"
  ],
  "rules": {
    "react/jsx-uses-react": "error",
    "react/jsx-uses-vars": "error",
  }
}

演示

这里我们来演示 eslint-plugin-vue 插件的使用。

首先初始化一个 Vue 的项目:

npm init vue@latest

接下来控制台会出现交互式选择,选择安装 ESLint 以及 Prettier。

eslint-plugin-vue 提供了一些预定义配置的选项,说明如下:

针对使用 Vue.js 3.x 的配置:

针对使用 Vue.js 2.x 的配置:

除了上述预设规则集外,也可以对某些规则进行自定义,如:

rules: {
  'vue/html-quotes': ['error', 'double'],
  'vue/html-self-closing': [
    'error',
    {
      html: {
        void: 'always',
        normal: 'never',
        component: 'always'
      },
      svg: 'always',
      math: 'always'
    }
  ]
},

自定义插件

ESLint 插件主要是用来扩展 ESLint 本身没有的功能,这里包括扩展规则、扩展配置、扩展解析器。

90% 的 ESLint 插件都是以扩展规则为主,所以这些插件里面会包含大量的自定义规则。

像这一类的插件,一般一条规则会对应一个 JS 文件,JS 文件里面需要导出一个对象:

官方文档

module.exports = {
  // 元数据信息
  meta: {
    
  },
  // 规则具体的实现
  create: function() {
    return {}
  }
}

源代码最终会被解析成一个 [[002.抽象语法树|抽象语法树]],这个抽象语法树就是一个树结构,里面由一个一个的节点组成,每一个节点是一个 token,工具在处理源码的时候,实际上就会去遍历这棵树,遍历到对应的节点,然后针对对应节点做出相应的处理。

上面这些方法所对应的名称实际上都来源于 ESTree 规范里面所定义的 AST 节点类型。

这些方法里面接收一个参数,该参数是当前所遍历到的 AST 节点对象,通过这个节点对象就可以拿到当前节点一些具体的信息以及该节点对应的子节点:

create: function() {
    return {
      CallExpression(node) {
        // ...
      }
    }
}

除了节点处理函数以外,create 方法还会自动传入一个 context 参数:

create: function(context) {
    return {
      CallExpression(node) {
        // ...
      }
    }
}

context 参数提供了一些方法:

实战案例

创建一个插件项目 eslint-plugin-customrules,使用 pnpm init 后在项目根目录中创建 rules 目录,该目录用于存放的自定义规则。

创建如下两条规则:

// 不允许有 alert 语句
// alert("xxx")
module.exports = {
  meta: {
    type: "problem",
    docs: {
      description: "disallow the use of alert",
      category: "Best Practices",
    },
    fixable: null,
  },
  create(context) {
    return {
      // 这个方法会在遍历到一个函数调用时被调用
      CallExpression(node) {
        if (node.callee.name === "alert") {
          // 说明当前是一个 alert 的函数调用
          context.report({
            node,
            message: "不允许出现 alert 语句",
          });
        }
      },
    };
  },
};
// 不允许有 console.log 语句
// console.log("xxx")
module.exports = {
  meta: {
    type: "problem",
    docs: {
      description: "disallow the use of console.log",
      category: "Best Practices",
    },
    fixable: null,
  },
  create(context) {
    return {
      CallExpression(node) {
        if (
          node.callee.object &&
          node.callee.object.name === "console" &&
          node.callee.property.name === "log"
        ) {
          context.report({
            node,
            message: "不允许出现 console.log 语句",
          });
        }
      },
    };
  },
};

每一条规则对应一个 JS 文件,JS 文件导出一个对象,对象里面包含了基本的 metacreate 配置项。

下一步在 package.json 中,添加 peerDependencies

"peerDependencies": {
  "eslint": "^7.0.0"
}

最后创建入口文件,用于将所有的规则导出:

// index.js
// 该文件是整个包的入口文件,用于导出所有的规则

module.exports = {
  rules: {
    // 规则名称: 规则文件
    "no-alert": require("./rules/no-alert"),
    "no-console-log": require("./rules/no-console-log"),
  },
};

测试

接下来需要测试这个插件。这里选择通过 npm link 的方式来进行本地链接:

来到插件的根目录,执行 npm link,这样该项目就会创建一个软链接到全局包目录里,其他项目就可以通过 link 的方式来链接这个包。

之后创建一个测试项目,初始化后安装 eslint 依赖并链接之前写的 eslint 插件:

npm i eslint -D
npm link eslint-plugin-customrules

配置文件 .eslintrc.json 中配置:

{
  "plugins": ["customrules"],
  "rules": {
    "customrules/no-console-log": "warn",
    "customrules/no-alert": "error"
  }
}

然后在 src/index.js 中书写一些测试代码:

alert("Hello");
console.log("World");

运行 npx eslint ./src 即可看到 Lint 错误信息。